load libraries

library(rtweet)
library(tidyverse)
# library(stringr)  
# library(tm)  #  text mining
# library(SnowballC)    # remove common word endings / Stemming
library(tidytext)
library(wordcloud2)

Get Tweets

search_tweets

Tokenize tweets

tweets_by_tweeter <- tweet_collection %>% 
  group_by(screen_name) %>% 
  mutate(line = row_number()) %>% 
  ungroup()

tweets_by_tweeter %>% 
  count(screen_name, sort = TRUE)

glimpse(tweets_by_tweeter)
Rows: 381
Columns: 91
$ user_id                 <chr> "729978319", "2842694434", "1109187936602472449", "26926933", "15919288...
$ status_id               <chr> "1321172260313415680", "1321162496166690819", "1321161276408516610", "1...
$ created_at              <dttm> 2020-10-27 19:29:47, 2020-10-27 18:50:59, 2020-10-27 18:46:08, 2020-10...
$ screen_name             <chr> "DrDanielKolder", "kschwartz827", "chrisgb002000", "CoachDouglas21", "O...
$ text                    <chr> "@BluejayMBB @j5_twann @Alex_OC11 @BIGEASTMBB @marchmadness @cucoachmac...
$ source                  <chr> "Twitter for iPhone", "Twitter for iPhone", "Twitter for Android", "Twi...
$ display_text_width      <dbl> 279, 43, 218, 22, 270, 50, 276, 259, 52, 63, 43, 177, 27, 103, 15, 101,...
$ reply_to_status_id      <chr> "1320769690394832896", "1321140791490703361", NA, NA, NA, NA, NA, NA, N...
$ reply_to_user_id        <chr> "325898875", "57422635", NA, NA, NA, NA, NA, NA, NA, NA, "202416362", "...
$ reply_to_screen_name    <chr> "BluejayMBB", "MSU_Basketball", NA, NA, NA, NA, NA, NA, NA, NA, "marchm...
$ is_quote                <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALS...
$ is_retweet              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, F...
$ favorite_count          <int> 1, 0, 0, 0, 2, 31, 19, 883, 1, 6, 0, 0, 1, 0, 0, 0, 7, 24, 9, 0, 0, 0, ...
$ retweet_count           <int> 0, 0, 0, 0, 1, 3, 6, 207, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 6, 0, 0, 1, 0, ...
$ quote_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ reply_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ hashtags                <list> [NA, NA, <"LifetimeLonghorn", "OneShiningMoment", "MarchMadness", "Hoo...
$ symbols                 <list> [NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N...
$ urls_url                <list> [NA, NA, NA, "twitter.com/marchmadness/s…", "osga.com/online_gaming_…"...
$ urls_t.co               <list> [NA, NA, NA, "https://t.co/YqppSGtY7N", "https://t.co/LRDrCdovtf", NA,...
$ urls_expanded_url       <list> [NA, NA, NA, "https://twitter.com/marchmadness/status/1318959234692927...
$ media_url               <list> [NA, NA, "http://pbs.twimg.com/ext_tw_video_thumb/1247537418003046407/...
$ media_t.co              <list> [NA, NA, "https://t.co/toBH5bKqyo", NA, NA, "https://t.co/pfRL2EOYSR",...
$ media_expanded_url      <list> [NA, NA, "https://twitter.com/chrisgb002000/status/1247537446960525323...
$ media_type              <list> [NA, NA, "photo", NA, NA, "photo", NA, "photo", NA, NA, NA, NA, NA, NA...
$ ext_media_url           <list> [NA, NA, "http://pbs.twimg.com/ext_tw_video_thumb/1247537418003046407/...
$ ext_media_t.co          <list> [NA, NA, "https://t.co/toBH5bKqyo", NA, NA, "https://t.co/pfRL2EOYSR",...
$ ext_media_expanded_url  <list> [NA, NA, "https://twitter.com/chrisgb002000/status/1247537446960525323...
$ ext_media_type          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ mentions_user_id        <list> [<"325898875", "3177384938", "2772222795", "1324557265", "202416362", ...
$ mentions_screen_name    <list> [<"BluejayMBB", "j5_twann", "Alex_OC11", "BIGEASTMBB", "marchmadness",...
$ lang                    <chr> "en", "en", "en", "en", "en", "en", "en", "en", "en", "en", "en", "en",...
$ quoted_status_id        <chr> NA, NA, NA, "1318959234692927488", NA, NA, NA, NA, "1321131895300804610...
$ quoted_text             <chr> NA, NA, NA, "#KatzRankz - #WestAward Candidates, as heard on the #MM365...
$ quoted_created_at       <dttm> NA, NA, NA, 2020-10-21 16:56:01, NA, NA, NA, NA, 2020-10-27 16:49:23, ...
$ quoted_source           <chr> NA, NA, NA, "Twitter Web App", NA, NA, NA, NA, "Twitter Media Studio", ...
$ quoted_favorite_count   <int> NA, NA, NA, 850, NA, NA, NA, NA, 31, 31, NA, NA, NA, NA, NA, NA, NA, 88...
$ quoted_retweet_count    <int> NA, NA, NA, 158, NA, NA, NA, NA, 3, 3, NA, NA, NA, NA, NA, NA, NA, 207,...
$ quoted_user_id          <chr> NA, NA, NA, "202416362", NA, NA, NA, NA, "202416362", "202416362", NA, ...
$ quoted_screen_name      <chr> NA, NA, NA, "marchmadness", NA, NA, NA, NA, "marchmadness", "marchmadne...
$ quoted_name             <chr> NA, NA, NA, "NCAA March Madness", NA, NA, NA, NA, "NCAA March Madness",...
$ quoted_followers_count  <int> NA, NA, NA, 1418660, NA, NA, NA, NA, 1418660, 1418660, NA, NA, NA, NA, ...
$ quoted_friends_count    <int> NA, NA, NA, 815, NA, NA, NA, NA, 815, 815, NA, NA, NA, NA, NA, NA, NA, ...
$ quoted_statuses_count   <int> NA, NA, NA, 29870, NA, NA, NA, NA, 29870, 29870, NA, NA, NA, NA, NA, NA...
$ quoted_location         <chr> NA, NA, NA, "", NA, NA, NA, NA, "", "", NA, NA, NA, NA, NA, NA, NA, "",...
$ quoted_description      <chr> NA, NA, NA, "The official NCAA March Madness destination for all things...
$ quoted_verified         <lgl> NA, NA, NA, TRUE, NA, NA, NA, NA, TRUE, TRUE, NA, NA, NA, NA, NA, NA, N...
$ retweet_status_id       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_text            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_created_at      <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
$ retweet_source          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_favorite_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_retweet_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_user_id         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_screen_name     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_name            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_followers_count <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_friends_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_statuses_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_location        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_description     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ retweet_verified        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ place_url               <chr> NA, NA, NA, NA, NA, NA, NA, NA, "https://api.twitter.com/1.1/geo/id/5c0...
$ place_name              <chr> NA, NA, NA, NA, NA, NA, NA, NA, "Buford", NA, NA, NA, NA, NA, NA, NA, N...
$ place_full_name         <chr> NA, NA, NA, NA, NA, NA, NA, NA, "Buford, GA", NA, NA, NA, NA, NA, NA, N...
$ place_type              <chr> NA, NA, NA, NA, NA, NA, NA, NA, "city", NA, NA, NA, NA, NA, NA, NA, NA,...
$ country                 <chr> NA, NA, NA, NA, NA, NA, NA, NA, "United States", NA, NA, NA, NA, NA, NA...
$ country_code            <chr> NA, NA, NA, NA, NA, NA, NA, NA, "US", NA, NA, NA, NA, NA, NA, NA, NA, N...
$ geo_coords              <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>,...
$ coords_coords           <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>,...
$ bbox_coords             <list> [<NA, NA, NA, NA, NA, NA, NA, NA>, <NA, NA, NA, NA, NA, NA, NA, NA>, <...
$ status_url              <chr> "https://twitter.com/DrDanielKolder/status/1321172260313415680", "https...
$ name                    <chr> "Dr. Daniel G. Kolder", "Kevin", "Chris Bennett (CB)", "CoachD", "OSGA"...
$ location                <chr> "Camarillo, CA", "", "Clovis/Germany/Cali/ATX/WA", "Oklahoma City, OK",...
$ description             <chr> "Southern California Plastic Surgeon", "Michigan fan, golfer, Daily Fan...
$ url                     <chr> "https://t.co/w4CTVrZdL9", NA, NA, NA, "https://t.co/EFlm6UqO6f", "http...
$ protected               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, F...
$ followers_count         <int> 1173, 5, 3390, 3814, 1732, 1418660, 1418660, 1418660, 4498, 1255, 308, ...
$ friends_count           <int> 133, 92, 4244, 1878, 4146, 815, 815, 815, 941, 872, 251, 4833, 120, 554...
$ listed_count            <int> 24, 0, 19, 43, 34, 3815, 3815, 3815, 18, 3, 0, 47, 2, 54, 7, 6807, 6807...
$ statuses_count          <int> 1704, 286, 131418, 331327, 8712, 29870, 29870, 29870, 4189, 1916, 2300,...
$ favourites_count        <int> 2351, 120, 331, 34345, 926, 1695, 1695, 1695, 1757, 18040, 4611, 57931,...
$ account_created_at      <dttm> 2012-08-01 04:38:50, 2014-10-24 17:07:44, 2019-03-22 20:19:46, 2009-03...
$ verified                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALS...
$ profile_url             <chr> "https://t.co/w4CTVrZdL9", NA, NA, NA, "https://t.co/EFlm6UqO6f", "http...
$ profile_expanded_url    <chr> "http://www.pacificaplasticsurgery.com", NA, NA, NA, "http://www.osga.c...
$ account_lang            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ profile_banner_url      <chr> "https://pbs.twimg.com/profile_banners/729978319/1518222766", NA, "http...
$ profile_background_url  <chr> "http://abs.twimg.com/images/themes/theme1/bg.png", "http://abs.twimg.c...
$ profile_image_url       <chr> "http://pbs.twimg.com/profile_images/714501386155782146/Ybmfn3B5_normal...
$ line                    <int> 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 1, 1, 2, 3, 4,...

"Because we have kept text such as hashtags and usernames in the dataset, we can’t use a simple anti_join() to remove stop words. Instead, we can take the approach shown in the filter() line that uses str_detect() from the stringr package. – https://www.tidytextmining.com/twitter.html

tweets_tokenized <- tweets_by_tweeter %>% 
  select(text, screen_name, line) %>% 
  unnest_tokens(word, text, token = "tweets") %>%
  filter(!word %in% stop_words$word,
         !word %in% str_remove_all(stop_words$word, "'"),
         str_detect(word, "[a-z]")) 

tweets_tokenized

Word frequencies

Calculate word frequency

frequency <- tweets_tokenized %>% 
  group_by(screen_name) %>% 
  count(word, sort = TRUE) %>% 
  left_join(tweets_tokenized %>% 
              group_by(screen_name) %>% 
              summarise(total = n())) %>%
  mutate(freq = n/total)
`summarise()` ungrouping output (override with `.groups` argument)
Joining, by = "screen_name"
frequency

"This is a nice and tidy data frame but we would actually like to plot those frequencies on the x- and y-axes of a plot, so we will need to use spread() from tidyr make a differently shaped data frame. – https://www.tidytextmining.com/twitter.html

pivot_wider

frequency <- frequency %>% 
  select(screen_name, word, freq) %>% 
  pivot_wider(names_from = screen_name, values_from = freq)

frequency


# frequency %>% 
#   select(screen_name, word, freq) %>% 
#   spread(screen_name, freq)

viz it

ggplot(frequency, aes(CBBCent1, Adam_Bradford14)) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.25, height = 0.25) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = scales::percent_format()) +
  scale_y_log10(labels = scales::percent_format()) +
  geom_abline(color = "firebrick")


# marchmadness  TheAndyKatz

ggplot(frequency, aes(marchmadness, TheAndyKatz)) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.25, height = 0.25) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = scales::percent_format()) +
  scale_y_log10(labels = scales::percent_format()) +
  geom_abline(color = "firebrick")

Word Usage

word_ratios <- tweets_tokenized %>%
  filter(screen_name == "CBBCent1" | screen_name == "Adam_Bradford14") %>% 
  filter(!str_detect(word, "^@")) %>%
  count(word, screen_name) %>%
  group_by(word) %>%
  filter(sum(n) >= 2) %>%
  ungroup() %>%
  pivot_wider(names_from = screen_name, values_from = n, values_fill = 0) %>%
  mutate_if(is.numeric, list(~(. + 1) / (sum(.) + 1))) %>%
  mutate(logratio = log(CBBCent1 / Adam_Bradford14)) %>%
  arrange(desc(logratio))

word_ratios

equal usage

word_ratios %>% 
  arrange(abs(logratio))
word_ratios %>%
  group_by(logratio < 0) %>%
  top_n(15, abs(logratio)) %>%
  ungroup() %>%
  mutate(word = reorder(word, logratio)) %>%
  ggplot(aes(word, logratio, fill = logratio < 0)) +
  geom_col() + #show.legend = FALSE) +
  coord_flip() +
  ylab("log odds ratio (CCBCent1/Adam_Bradford14)") +
  scale_fill_discrete(name = "", labels = c("CCBCent1", "Adam_Bradford14"))

Text Mining – Data Treamtement


# make lower case
corpus.prep <- tm_map(dfCorpus, str_to_lower)  # stringr::str_to_lower() instead of base::tolower

# remove white space
corpus.prep <- tm_map(corpus.prep, stripWhitespace) 

# remove punctuation 
corpus.prep <- tm_map(corpus.prep, removePunctuation)

# remove numbers
corpus.prep <- tm_map(corpus.prep, removeNumbers) 

# head(stopwords("english"))

# remove stop words 
corpus <- tm_map(corpus.prep, removeWords, stopwords("english")) 
# docs <- tm_map(docs, removeWords, c("department", "email"))   

# stem remaining words
corpus <- tm_map(corpus, stemDocument)  # snowballC::StemDocument() 

Make Term Document Matrix

# dtm <- DocumentTermMatrix(docs) 

dtm2 <- TermDocumentMatrix(corpus)
m <- as.matrix(dtm2)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)

d <- d %>% 
  slice(2:200)

Wordcloud2


wordcloud2(d, color = "random-dark", backgroundColor = "orange")

wordcloud2(d, size = 0.3, shape="star", color = "random-light", backgroundColor = 'black', fontFamily="Loma")

# letterCloud(d, word="R", size = 1, fontFamily="Loma", backgroundColor = 'black')

Resource list

LS0tDQp0aXRsZTogIlJ0d2VldCINCnN1YnRpdGxlOiAiYW4gUmZ1biBkZW1vbnN0cmF0aW9uIg0KYXV0aG9yOiAiSm9obiBMaXR0bGUiDQpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBsb2FkIGxpYnJhcmllcw0KDQpgYGB7ciBsaWJyYXJpZXMsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJ0d2VldCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBsaWJyYXJ5KHN0cmluZ3IpICANCiMgbGlicmFyeSh0bSkgICMgIHRleHQgbWluaW5nDQojIGxpYnJhcnkoU25vd2JhbGxDKSAgICAjIHJlbW92ZSBjb21tb24gd29yZCBlbmRpbmdzIC8gU3RlbW1pbmcNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHdvcmRjbG91ZDIpDQpgYGANCg0KDQojIyBHZXQgVHdlZXRzIA0KDQpzZWFyY2hfdHdlZXRzDQpgYGB7ciBnZXRUd2VldHN9DQp0d2VldF9jb2xsZWN0aW9uIDwtIHNlYXJjaF90d2VldHMoIm1hcmNobWFkbmVzcyIsIG49MTAwMCwgbGFuZyA9ICJlbiIpDQp0d2VldF9jb2xsZWN0aW9uIDwtIHR3ZWV0X2NvbGxlY3Rpb24gJT4lIA0KICBmaWx0ZXIoaXNfcmV0d2VldCA9PSAiRkFMU0UiKQ0KdHdlZXRfY29sbGVjdGlvbg0KYGBgDQoNCg0KIyMgVG9rZW5pemUgdHdlZXRzDQoNCmBgYHtyIGNvcnB1czJ2ZWN0b3J9DQp0d2VldHNfYnlfdHdlZXRlciA8LSB0d2VldF9jb2xsZWN0aW9uICU+JSANCiAgZ3JvdXBfYnkoc2NyZWVuX25hbWUpICU+JSANCiAgbXV0YXRlKGxpbmUgPSByb3dfbnVtYmVyKCkpICU+JSANCiAgdW5ncm91cCgpDQoNCnR3ZWV0c19ieV90d2VldGVyICU+JSANCiAgY291bnQoc2NyZWVuX25hbWUsIHNvcnQgPSBUUlVFKQ0KDQpnbGltcHNlKHR3ZWV0c19ieV90d2VldGVyKQ0KYGBgDQoNCg0KPiAiQmVjYXVzZSB3ZSBoYXZlIGtlcHQgdGV4dCBzdWNoIGFzIGhhc2h0YWdzIGFuZCB1c2VybmFtZXMgaW4gdGhlIGRhdGFzZXQsIHdlIGNhbuKAmXQgdXNlIGEgc2ltcGxlIGFudGlfam9pbigpIHRvIHJlbW92ZSBzdG9wIHdvcmRzLiBJbnN0ZWFkLCB3ZSBjYW4gdGFrZSB0aGUgYXBwcm9hY2ggc2hvd24gaW4gdGhlIGZpbHRlcigpIGxpbmUgdGhhdCB1c2VzIHN0cl9kZXRlY3QoKSBmcm9tIHRoZSBzdHJpbmdyIHBhY2thZ2UuIC0tIGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS90d2l0dGVyLmh0bWwNCg0KDQpgYGB7ciB0b2tlbml6ZWQgdHdlZXRzfQ0KdHdlZXRzX3Rva2VuaXplZCA8LSB0d2VldHNfYnlfdHdlZXRlciAlPiUgDQogIHNlbGVjdCh0ZXh0LCBzY3JlZW5fbmFtZSwgbGluZSkgJT4lIA0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQsIHRva2VuID0gInR3ZWV0cyIpICU+JQ0KICBmaWx0ZXIoIXdvcmQgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZCAlaW4lIHN0cl9yZW1vdmVfYWxsKHN0b3Bfd29yZHMkd29yZCwgIiciKSwNCiAgICAgICAgIHN0cl9kZXRlY3Qod29yZCwgIlthLXpdIikpIA0KDQp0d2VldHNfdG9rZW5pemVkDQpgYGANCg0KIyMgV29yZCBmcmVxdWVuY2llcw0KDQojIyMgQ2FsY3VsYXRlIHdvcmQgZnJlcXVlbmN5DQoNCmBgYHtyfQ0KZnJlcXVlbmN5IDwtIHR3ZWV0c190b2tlbml6ZWQgJT4lIA0KICBncm91cF9ieShzY3JlZW5fbmFtZSkgJT4lIA0KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBsZWZ0X2pvaW4odHdlZXRzX3Rva2VuaXplZCAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHNjcmVlbl9uYW1lKSAlPiUgDQogICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSkpICU+JQ0KICBtdXRhdGUoZnJlcSA9IG4vdG90YWwpDQoNCmZyZXF1ZW5jeQ0KYGBgDQoNCj4gIlRoaXMgaXMgYSBuaWNlIGFuZCB0aWR5IGRhdGEgZnJhbWUgYnV0IHdlIHdvdWxkIGFjdHVhbGx5IGxpa2UgdG8gcGxvdCB0aG9zZSBmcmVxdWVuY2llcyBvbiB0aGUgeC0gYW5kIHktYXhlcyBvZiBhIHBsb3QsIHNvIHdlIHdpbGwgbmVlZCB0byB1c2Ugc3ByZWFkKCkgZnJvbSB0aWR5ciBtYWtlIGEgZGlmZmVyZW50bHkgc2hhcGVkIGRhdGEgZnJhbWUuIC0tIGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS90d2l0dGVyLmh0bWwNCg0KcGl2b3Rfd2lkZXINCg0KYGBge3J9DQpmcmVxdWVuY3kgPC0gZnJlcXVlbmN5ICU+JSANCiAgc2VsZWN0KHNjcmVlbl9uYW1lLCB3b3JkLCBmcmVxKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzY3JlZW5fbmFtZSwgdmFsdWVzX2Zyb20gPSBmcmVxKQ0KDQpmcmVxdWVuY3kNCg0KDQojIGZyZXF1ZW5jeSAlPiUgDQojICAgc2VsZWN0KHNjcmVlbl9uYW1lLCB3b3JkLCBmcmVxKSAlPiUgDQojICAgc3ByZWFkKHNjcmVlbl9uYW1lLCBmcmVxKQ0KYGBgDQoNCiMjIyB2aXogaXQNCg0KYGBge3Igd29yZF9mcmVxIHBsb3R9DQpnZ3Bsb3QoZnJlcXVlbmN5LCBhZXMoQ0JCQ2VudDEsIEFkYW1fQnJhZGZvcmQxNCkpICsNCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHNpemUgPSAyLjUsIHdpZHRoID0gMC4yNSwgaGVpZ2h0ID0gMC4yNSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCksIGNoZWNrX292ZXJsYXAgPSBUUlVFLCB2anVzdCA9IDEuNSkgKw0KICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKw0KICBzY2FsZV95X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKw0KICBnZW9tX2FibGluZShjb2xvciA9ICJmaXJlYnJpY2siKQ0KDQojIG1hcmNobWFkbmVzcyAgVGhlQW5keUthdHoNCg0KZ2dwbG90KGZyZXF1ZW5jeSwgYWVzKG1hcmNobWFkbmVzcywgVGhlQW5keUthdHopKSArDQogIGdlb21faml0dGVyKGFscGhhID0gMC4xLCBzaXplID0gMi41LCB3aWR0aCA9IDAuMjUsIGhlaWdodCA9IDAuMjUpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdvcmQpLCBjaGVja19vdmVybGFwID0gVFJVRSwgdmp1c3QgPSAxLjUpICsNCiAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsNCiAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsNCiAgZ2VvbV9hYmxpbmUoY29sb3IgPSAiZmlyZWJyaWNrIikNCmBgYA0KDQoNCiMjIFdvcmQgVXNhZ2UNCg0KDQpgYGB7cn0NCiMgZ2xpbXBzZSh0d2VldHNfYnlfdHdlZXRlcikgICMgY3JlYXRlZF9hdA0KDQp0d2VldHNfYnlfdHdlZXRlciAlPiUgDQogIHN1bW1hcmlzZShtaW5fZGF0ZSA9IG1pbihjcmVhdGVkX2F0KSwgbWF4X2RhdGUgPSBtYXgoY3JlYXRlZF9hdCkpDQoNCg0KYGBgDQoNCg0KYGBge3J9DQp3b3JkX3JhdGlvcyA8LSB0d2VldHNfdG9rZW5pemVkICU+JQ0KICBmaWx0ZXIoc2NyZWVuX25hbWUgPT0gIkNCQkNlbnQxIiB8IHNjcmVlbl9uYW1lID09ICJBZGFtX0JyYWRmb3JkMTQiKSAlPiUgDQogIGZpbHRlcighc3RyX2RldGVjdCh3b3JkLCAiXkAiKSkgJT4lDQogIGNvdW50KHdvcmQsIHNjcmVlbl9uYW1lKSAlPiUNCiAgZ3JvdXBfYnkod29yZCkgJT4lDQogIGZpbHRlcihzdW0obikgPj0gMikgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNjcmVlbl9uYW1lLCB2YWx1ZXNfZnJvbSA9IG4sIHZhbHVlc19maWxsID0gMCkgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCBsaXN0KH4oLiArIDEpIC8gKHN1bSguKSArIDEpKSkgJT4lDQogIG11dGF0ZShsb2dyYXRpbyA9IGxvZyhDQkJDZW50MSAvIEFkYW1fQnJhZGZvcmQxNCkpICU+JQ0KICBhcnJhbmdlKGRlc2MobG9ncmF0aW8pKQ0KDQp3b3JkX3JhdGlvcw0KYGBgDQoNCiMjIyBlcXVhbCB1c2FnZQ0KDQpgYGB7cn0NCndvcmRfcmF0aW9zICU+JSANCiAgYXJyYW5nZShhYnMobG9ncmF0aW8pKQ0KYGBgDQoNCg0KYGBge3J9DQp3b3JkX3JhdGlvcyAlPiUNCiAgZ3JvdXBfYnkobG9ncmF0aW8gPCAwKSAlPiUNCiAgdG9wX24oMTUsIGFicyhsb2dyYXRpbykpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBsb2dyYXRpbykpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQsIGxvZ3JhdGlvLCBmaWxsID0gbG9ncmF0aW8gPCAwKSkgKw0KICBnZW9tX2NvbCgpICsgI3Nob3cubGVnZW5kID0gRkFMU0UpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgeWxhYigibG9nIG9kZHMgcmF0aW8gKENDQkNlbnQxL0FkYW1fQnJhZGZvcmQxNCkiKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICIiLCBsYWJlbHMgPSBjKCJDQ0JDZW50MSIsICJBZGFtX0JyYWRmb3JkMTQiKSkNCmBgYA0KDQoNCiMjIFRleHQgTWluaW5nIC0tIERhdGEgVHJlYW10ZW1lbnQgDQpgYGB7ciB0aWR5VGhlVGV4dH0NCg0KIyBtYWtlIGxvd2VyIGNhc2UNCmNvcnB1cy5wcmVwIDwtIHRtX21hcChkZkNvcnB1cywgc3RyX3RvX2xvd2VyKSAgIyBzdHJpbmdyOjpzdHJfdG9fbG93ZXIoKSBpbnN0ZWFkIG9mIGJhc2U6OnRvbG93ZXINCg0KIyByZW1vdmUgd2hpdGUgc3BhY2UNCmNvcnB1cy5wcmVwIDwtIHRtX21hcChjb3JwdXMucHJlcCwgc3RyaXBXaGl0ZXNwYWNlKSANCg0KIyByZW1vdmUgcHVuY3R1YXRpb24gDQpjb3JwdXMucHJlcCA8LSB0bV9tYXAoY29ycHVzLnByZXAsIHJlbW92ZVB1bmN0dWF0aW9uKQ0KDQojIHJlbW92ZSBudW1iZXJzDQpjb3JwdXMucHJlcCA8LSB0bV9tYXAoY29ycHVzLnByZXAsIHJlbW92ZU51bWJlcnMpIA0KDQojIGhlYWQoc3RvcHdvcmRzKCJlbmdsaXNoIikpDQoNCiMgcmVtb3ZlIHN0b3Agd29yZHMgDQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cy5wcmVwLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpIA0KIyBkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVXb3JkcywgYygiZGVwYXJ0bWVudCIsICJlbWFpbCIpKSAgIA0KDQojIHN0ZW0gcmVtYWluaW5nIHdvcmRzDQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgc3RlbURvY3VtZW50KSAgIyBzbm93YmFsbEM6OlN0ZW1Eb2N1bWVudCgpIA0KDQpgYGANCg0KDQojIyBNYWtlIFRlcm0gRG9jdW1lbnQgTWF0cml4DQpgYGB7cn0NCiMgZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChkb2NzKSANCg0KZHRtMiA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzKQ0KbSA8LSBhcy5tYXRyaXgoZHRtMikNCnYgPC0gc29ydChyb3dTdW1zKG0pLGRlY3JlYXNpbmc9VFJVRSkNCmQgPC0gZGF0YS5mcmFtZSh3b3JkID0gbmFtZXModiksZnJlcT12KQ0KDQpkIDwtIGQgJT4lIA0KICBzbGljZSgyOjIwMCkNCg0KYGBgDQoNCiMjIFdvcmRjbG91ZDINCmBgYHtyfQ0KDQp3b3JkY2xvdWQyKGQsIGNvbG9yID0gInJhbmRvbS1kYXJrIiwgYmFja2dyb3VuZENvbG9yID0gIm9yYW5nZSIpDQoNCndvcmRjbG91ZDIoZCwgc2l6ZSA9IDAuMywgc2hhcGU9InN0YXIiLCBjb2xvciA9ICJyYW5kb20tbGlnaHQiLCBiYWNrZ3JvdW5kQ29sb3IgPSAnYmxhY2snLCBmb250RmFtaWx5PSJMb21hIikNCg0KIyBsZXR0ZXJDbG91ZChkLCB3b3JkPSJSIiwgc2l6ZSA9IDEsIGZvbnRGYW1pbHk9IkxvbWEiLCBiYWNrZ3JvdW5kQ29sb3IgPSAnYmxhY2snKQ0KDQpgYGANCg0KIyMgUmVzb3VyY2UgbGlzdA0KDQotIGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS90ZXh0LW1pbmluZy1hbmQtd29yZC1jbG91ZC1mdW5kYW1lbnRhbHMtaW4tci01LXNpbXBsZS1zdGVwcy15b3Utc2hvdWxkLWtub3cNCg0KLSBodHRwOi8vYW50b25pby1mZXJyYXJvLmV1LnBuL3dvcmQtY2xvdWRzLWluLXItcGFja2FnZXMtd29yZGNsb3VkMi1hbmQtdG0vDQoNCi0gaHR0cHM6Ly9qcm5vbGQuZ2l0aHViLmlvL3Fzcy10aWR5L2Rpc2NvdmVyeS5odG1sI3RleHR1YWwtZGF0YQ0KDQotIGh0dHBzOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzMxODY3XzgyMzY5ODdjZjBhODQ0NGU5NjJjY2QyYWVjNDZkOWMzLmh0bWwNCg0KLSBvZiBsZXNzIHVzZQ0KDQogICAgLSBodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL01hbmlwdWxhdGluZ19kYXRhL0NvbnZlcnRpbmdfYmV0d2Vlbl9kYXRhX2ZyYW1lc19hbmRfY29udGluZ2VuY3lfdGFibGVzLw0KICAgIC0gaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vaG93LXRvLWdldC10aGUtZnJlcXVlbmN5LXRhYmxlLW9mLWEtY2F0ZWdvcmljYWwtdmFyaWFibGUtYXMtYS1kYXRhLWZyYW1lLWluLXIvDQogICAgLSBodHRwczovL3d3dy5xdW9yYS5jb20vSG93LWRvLUktZ2V0LWEtZnJlcXVlbmN5LWNvdW50LWJhc2VkLW9uLXR3by1jb2x1bW5zLXZhcmlhYmxlcy1pbi1hbi1SLWRhdGFmcmFtZQ0KICAgIC0gaHR0cHM6Ly93d3cucXVvcmEuY29tL0hvdy1kby15b3UtY3JlYXRlLWEtY29ycHVzLWZyb20tYS1kYXRhLWZyYW1lLWluLVINCiAgICANCg0K